using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.IO;

namespace asn_compile_cs
{
    class CSharp_CodeEmitter : CodeEmitter
    {
        StreamWriter fileOut;

        public CSharp_CodeEmitter(string fileName, CmdLineArgs args)
        {
            fileOut = File.CreateText(fileName + ".cs");
            EmitHeader();
        }

        override public void Close()
        {
            EmitFooter();
            fileOut.Close();
        }

        public void EmitHeader()
        {
            fileOut.WriteLine("using System;");
            fileOut.WriteLine("");
        }

        public void EmitFooter()
        {
            if (m_szLastNamespace != null) {
                fileOut.WriteLine("}");
            }
            fileOut.WriteLine("");
        }

        static string m_szLastNamespace;

        private void EmitNamespace(string szNameSpace)
        {
            //
            //  If we are in the same name space, then nothing to emit
            //

            if (szNameSpace == m_szLastNamespace) return;

            //
            //  If there was a previous name space, then close it
            //

            if (m_szLastNamespace != null) {
                fileOut.WriteLine("}");
                fileOut.WriteLine("");
            }

            //
            //  Emit the new namespace
            //

            if (szNameSpace == "") {
                fileOut.WriteLine("namespace A2C {");
            }
            else {
                fileOut.WriteLine("namespace {0} {{", szNameSpace);
            }
            m_szLastNamespace = szNameSpace;
        }

        override public void EmitSymbol(Symbol sym)
        {

            //
            //  Thanks to the C# compiler, we never have to recurse back into here
            //  so all of the code deailing with recusion in the C emitter can be omitted here.
            //

            switch (sym.GetSymbolType()) {
            case SymbolType.Module:
                //  If we don't have a namespace on the module, make it "A2C"
                //

                //  We don't do anything to emit modules.
                break;

                //
                //  Parameterized items are not directly emitted.
                //
            case SymbolType.ParameterizedType:
                break;

            case SymbolType.Type:
                EmitType(sym);
                break;

            case SymbolType.Value:
                EmitValue(sym);
                break;

            default:
                Debug.Assert(false);
                break;
            }
        }

        public void EmitType(Symbol sym)
        {
            //
            //  Put out the namespace if necessary.
            //

            EmitNamespace(sym.module.nameSpace);

            //
            //  Emit the starting line for the type.
            //  1.  We use partial so that other can also write functions that go into this class.
            //      With the partial keyword, the other functions can be in seperate files.
            //  2.  We always derive from a base class.  The base class will be one of our internal base
            //      classes normally, but could be another base class here for such items as constraints.
            //

            fileOut.WriteLine("public partial class {0} : A2C.{1} {{", sym.name, "FOO");


            //
            //  Emit out parts and pieces of this type
            //

            if (sym.symlstParts.Count > 0) {
                foreach (Symbol symT in sym.symlstParts) {
                    EmitSymbol(symT);
                }
            }

            //
            //  Emit out the data portions of the class
            //

            //
            //  Write out any constructors
            //

            fileOut.WriteLine("{1}() {{ }}", sym.name);

            //
            //  Write out any data accessors
            //

            //
            //  Write out any overridden functions
            //

#if false
            int cb;
            Type typ = sym.type;

            switch (typ.baseType) {
            case BaseType.Integer:
#if false
                if (typ.constraint != null) {
                    cb = typ.constraint.Size();
                }
                else {
                    cb = -1;
                }
#else
                cb =-1;
#endif

                if ((cb < 0) || (cb > 4)) {
                    fileOut.WriteLine("class " + sym.name + " : ASN_Base.ASN_HugeInteger");
                }
                else {
                    fileOut.WriteLine("class " + sym.name + " : ASN_Base.ASN_Integer");
                }
                fileOut.WriteLine("{");
                fileOut.WriteLine("\t/* Emit Constraints if necessary */");
                
                // If we have a list of symbols - emit them as well

                if (sym.type.fieldList != null) {
                    foreach (Symbol sym2 in sym.type.fieldList.list) {
                        EmitSymbol(sym2);
                    }
                }

                //  End the class

                fileOut.WriteLine("}");
                fileOut.WriteLine("");

                break;
            }
#endif
            fileOut.WriteLine("}");
        }

        public void EmitValue(Symbol sym)
        {
#if false
            switch (sym.type.baseType) {
            case BaseType.Integer:
                fileOut.WriteLine("\tASN_Base.ASN_Integer " + sym.name + " = new ASN_Base.ASN_Integer(" + sym.value.ToString() + ");");
                fileOut.WriteLine("");
                break;
            }
#endif
        }
    }
}
